package com.finance.system.accountbook.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.finance.system.accountbook.entity.CashRecordEntity;
import com.finance.system.accountbook.mapper.CashRecordMapper;
import com.finance.system.accountbook.service.ICashRecordService;
import com.finance.system.accountbook.transform.CashRecordTransform;
import com.finance.system.support.bean.OffsetQuery;
import com.finance.system.support.bean.OffsetResult;
import com.finance.system.support.bean.ResultBean;
import com.finance.system.support.bean.ResultConstant;
import com.finance.system.accountbook.entity.CashRecordInbound;
import com.finance.system.accountbook.entity.BalanceResult;
import com.finance.system.accountbook.entity.Strategy;
import com.finance.system.accountbook.entity.CashRecordOutbound;
import com.finance.system.support.BaseServiceImpl;
import com.finance.system.user.entity.GroupEntity;
import com.finance.system.user.entity.UserEntity;
import com.finance.system.user.entity.UserGroupRelationEntity;
import com.finance.system.user.service.IGroupService;
import com.finance.system.user.service.IUserGroupRelationService;
import com.finance.system.user.service.IUserService;
import com.finance.system.support.utils.DomainTransHelper;
import com.finance.system.support.utils.TimeFormatUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.groupingBy;

/**
 * <p>
 * 消费记录表，存放每笔消费的信息 服务实现类
 * </p>
 *
 * @author whdong
 * @since 2019-12-24
 */
@Service
public class CashRecordServiceImpl extends BaseServiceImpl<CashRecordMapper, CashRecordEntity> implements ICashRecordService {
    @Autowired
    private ICashRecordService cashRecordService;
    @Autowired
    private IUserService userService;
    @Autowired
    private IUserGroupRelationService relationService;
    @Autowired
    private IGroupService groupService;
    @Override
    public ResultBean<CashRecordEntity> addOneRecord(CashRecordInbound cashRecord) {
        if(cashRecord.getGroupId() == null){
            return ResultBean.create(ResultConstant.ERROR_CODE,ResultConstant.ERROR_MESSAGE_NO_GROUP_ID,null);
        }
        if(cashRecord.getUserId() == null){
            return ResultBean.create(ResultConstant.ERROR_CODE,ResultConstant.ERROR_MESSAGE_NO_USER_ID,null);
        }
        CashRecordEntity cashRecordEntity = DomainTransHelper.transFromTo(cashRecord, CashRecordEntity.class);
        cashRecordService.save(cashRecordEntity);
        return ResultBean.create(ResultConstant.SUCCESS_CODE,ResultConstant.ADD_SUCCESS_MESSAGE,cashRecordEntity);
    }

    @Override
    public ResultBean<CashRecordOutbound> getRecordById(String id) {
        CashRecordEntity cashRecord = this.getById(id);
        if(cashRecord == null){
            return ResultBean.create(ResultConstant.ERROR_CODE,ResultConstant.FAILURE_MESSAGE_NO_ID,null);
        }
        CashRecordOutbound outbound = DomainTransHelper.transFromTo(cashRecord, CashRecordOutbound.class);
        getUserInfo(Arrays.asList(outbound));

        return ResultBean.create(ResultConstant.SUCCESS_CODE,ResultConstant.SUCCESS_MESSAGE, outbound);
    }

    @Override
    public ResultBean<CashRecordEntity> modifyById(CashRecordInbound cashRecord) {
        if(cashRecord.getId() == null){
            return ResultBean.create(ResultConstant.ERROR_CODE,ResultConstant.FAILURE_MESSAGE_NO_ID,null);
        }
        CashRecordEntity cashRecordEntity = DomainTransHelper.transFromTo(cashRecord, CashRecordEntity.class);
        cashRecordService.save(cashRecordEntity);
        return ResultBean.create(ResultConstant.SUCCESS_CODE,ResultConstant.UPDATE_SUCCESS_MESSAGE,cashRecordEntity);
    }

    @Override
    public OffsetResult<CashRecordOutbound> getByCondition(CashRecordInbound cashRecord,
                                                           OffsetQuery offsetQuery) {
        OffsetResult<CashRecordEntity> offsetResult = cashRecordService.getListByCondition(cashRecord,
                offsetQuery);
        OffsetResult result = OffsetResult.changeRecords(offsetResult, CashRecordTransform.entityToOutBound(offsetResult.getRecords()));
        getUserInfo(result.getRecords());
        return result;
    }

    @Override
    public void removeById(String id) {
        cashRecordService.removeById(id);
    }

    @Override
    public OffsetResult<CashRecordOutbound> getPageDataByGroup(CashRecordInbound record,OffsetQuery<CashRecordEntity> offsetQuery) {
        if(record.getGroupId() == null){
            return OffsetResult.build(offsetQuery,
                    CashRecordOutbound.class);
        }
        OffsetResult<CashRecordEntity> offsetResult = cashRecordService.getListByCondition(record,
                offsetQuery);
        OffsetResult result = OffsetResult.changeRecords(offsetResult, CashRecordTransform.entityToOutBound(offsetResult.getRecords()));
        //获取用户信息
        getUserInfo(result.getRecords());
        return result;
    }


    @Override
    public ResultBean<Map<String, Object>> getSpendingStatisticsByGroup(CashRecordInbound record, OffsetQuery<CashRecordEntity> offsetQuery) {
        if(record.getGroupId() == null){
            return ResultBean.create(ResultConstant.ERROR_CODE,ResultConstant.ERROR_MESSAGE_NO_GROUP_ID, Collections.emptyMap());
        }
        OffsetResult<CashRecordEntity> offsetResult = cashRecordService.getListByCondition(record,
                offsetQuery);

        if(CollectionUtils.isEmpty(offsetResult.getRecords())){
            return ResultBean.create(ResultConstant.SUCCESS_CODE,ResultConstant.SUCCESS_MESSAGE, Collections.emptyMap());
        }
        OffsetResult result = OffsetResult.changeRecords(offsetResult, CashRecordTransform.entityToOutBound(offsetResult.getRecords()));
        //设置返回结果
        Map<String,Object> dataMap = new HashMap<>(8);
        //消费种类设置
        getConsumeTypeDistribution(dataMap,result.getRecords());
        //最贵交易设置
        getSelfMostExpensiveRecord(dataMap,result.getRecords(),record);
        //成员最贵交易统计
        getMostExpensiveRecordOfMember(dataMap,result.getRecords(),record);
        //收支累计
        getStatisticOfIncomeAndExpenditure(dataMap,result.getRecords(),record);
        return ResultBean.create(ResultConstant.SUCCESS_CODE,ResultConstant.SUCCESS_MESSAGE,dataMap);
    }

    @Override
    public ResultBean closeRecordOfSpendingStatisticsByGroup(CashRecordInbound record) {
        if(record.getGroupId() == null){
            return ResultBean.create(ResultConstant.ERROR_CODE,ResultConstant.ERROR_MESSAGE_NO_GROUP_ID,null);
        }
        List<CashRecordOutbound> cashRecords = cashRecordService.listByCondition(record);
        if(CollectionUtils.isEmpty(cashRecords)){
            return ResultBean.create(ResultConstant.SUCCESS_CODE,ResultConstant.SUCCESS_MESSAGE,null);
        }
        UpdateWrapper<CashRecordEntity> wrapper = new UpdateWrapper<>();
        wrapper.in("id",cashRecords.stream().map(CashRecordOutbound::getId).collect(Collectors.toList()));
        cashRecordService.modifyByWrapper(wrapper,DomainTransHelper.transFromTo(record,
                CashRecordEntity.class));
        return ResultBean.create(ResultConstant.SUCCESS_CODE,ResultConstant.SUCCESS_MESSAGE,null);
    }

    @Override
    public OffsetResult<CashRecordEntity> getListByCondition(CashRecordInbound record,
                                                                                              OffsetQuery offsetQuery) {
        QueryWrapper queryWrapper = getQueryWrapperByCondition(record);
        IPage page = baseMapper.selectPage(offsetQuery, queryWrapper);
        return OffsetResult.build(page);
    }

    @Override
    public List<CashRecordOutbound> listByCondition(CashRecordInbound record) {
        QueryWrapper queryWrapper = getQueryWrapperByCondition(record);
        return baseMapper.selectList(queryWrapper);
    }

    private QueryWrapper getQueryWrapperByCondition(CashRecordInbound record) {
        QueryWrapper queryWrapper = new QueryWrapper<CashRecordEntity>();
        if(record.getStartTime() != null){
            queryWrapper.gt("consume_date",
                    TimeFormatUtil.formatDateYYYYMMDDHHMMSS(record.getStartTime()));
        }
        if(record.getEndTime() != null){
            queryWrapper.lt("consume_date",
                    TimeFormatUtil.formatDateYYYYMMDDHHMMSS(record.getEndTime()));
        }
        return queryWrapper;
    }

    @Override
    public void modifyByWrapper(UpdateWrapper<CashRecordEntity> example, CashRecordEntity record) {
        baseMapper.update(record,example);
    }

    //先获取组内的所有成员，每条记录中每个人的消费累积，每个人的支出累积，计算每个人的应收钱数
    private void getStatisticOfIncomeAndExpenditure(Map<String, Object> dataMap,
                                                    List<CashRecordOutbound> cashRecords,
                                                    CashRecordInbound record) {
        //获取组内的所有成员
        List<UserEntity> members = getMemberOfGroup(record.getGroupId());
        List<BalanceResult> balanceResults = new ArrayList<>(members.size());
        for(UserEntity user :members){
            //查询当前人的支出累积
            List<CashRecordOutbound> recordsOfOneMemberCreate = cashRecords.stream().filter
                    (cashRecord -> cashRecord.getUserId() == user.getId()).collect(Collectors.toList());
            BigDecimal amountOfMember = recordsOfOneMemberCreate.stream().map(CashRecordOutbound::getAmount).reduce(BigDecimal.ZERO, (o1, o2) -> o1.add(o2));
            //查询当前人的消费累积

            //拿到该用户参与的账目记录
            List<CashRecordOutbound> recordsOfOneMemberParticipate = cashRecords.stream().filter
                    (cashRecord -> cashRecord.getParticipant().contains(user.getId().toString())).collect(Collectors.toList());
            BigDecimal amountOfMemberConsume = BigDecimal.ZERO;
            for(CashRecordOutbound cashRecord : recordsOfOneMemberParticipate){
                String[] split = cashRecord.getParticipant().split(",");
                BigDecimal avg = cashRecord.getAmount().divide(BigDecimal.valueOf(split.length));
                amountOfMemberConsume = avg.add(avg);
            }
            BigDecimal needGet = amountOfMember.subtract(amountOfMemberConsume);
            balanceResults.add(BalanceResult.create(StringUtils.isEmpty(user.getUserName()) ?
                    user.getPhone() : user.getUserName(),amountOfMember,amountOfMemberConsume,needGet));
        }
        //建议内容设置
        List<BalanceResult> results = getAddviceInfo(balanceResults);
        dataMap.put("balanceResults",results);
    }

    private List<BalanceResult> getAddviceInfo(List<BalanceResult> balanceResults) {
        List<BalanceResult> results = new ArrayList<>(balanceResults.size());
        Queue<BalanceResult> needPayList = new LinkedList();
        Queue<BalanceResult> needGetList = new LinkedList();
        //将需支付的和需收取的分成两个队列
        for(BalanceResult result : balanceResults){
            if(result.getNeedGet().compareTo(BigDecimal.ZERO) == -1){
                result.setNeedGet(result.getNeedGet().abs());
                needPayList.add(result);
            }
            if(result.getNeedGet().compareTo(BigDecimal.ZERO) == 1){
                needGetList.add(result);
            }
        }
        Iterator<BalanceResult> needGetIterator = needGetList.iterator();
        Iterator<BalanceResult> needPayIterator = needPayList.iterator();
        while(needGetIterator.hasNext()){
            BalanceResult needGetResult = needGetIterator.next();
            if(needGetResult.getNeedGet().equals(BigDecimal.ZERO)){
                continue;
            }
            List<Strategy> strategys = new ArrayList<>(balanceResults.size());
            while(true){
                BalanceResult needPayResult = needPayIterator.next();
                if(needPayResult.getNeedGet().abs().compareTo(needGetResult.getNeedGet()) == 0){//值相等，两者都弹出，新增一条策略
                    //新增一条策略
                    strategys.add(Strategy.create(needPayResult.getUserName(),needGetResult.getUserName(),needPayResult.getNeedGet().abs()));
                    //将策略绑定在需要支付的结果中
                    needPayResult.setAddvice(strategys);
                    //将本次的支付方的结果存起来
                    results.add(needPayResult);
                    //两个元素统一移除
                    needGetIterator.remove();
                    needPayIterator.remove();
                    break;
                }else if(needPayResult.getNeedGet().abs().compareTo(needGetResult.getNeedGet()) == -1){ //需要支付的一方不足与需要得到的一方
                    //新增一条策略
                    strategys.add(Strategy.create(needPayResult.getUserName(),needGetResult.getUserName(),needPayResult.getNeedGet().abs()));
                    //更新需要得到的一方
                    needGetResult.setNeedGet(needGetResult.getNeedGet().add(needPayResult.getNeedGet()));
                    //将策略绑定在需要支付的结果中
                    needPayResult.setAddvice(strategys);
                    //将本次的支付方的结果存起来
                    results.add(needPayResult);
                    //移除需要支付的一方
                    needPayIterator.remove();
                    break;
                }else{ //需要支付的一方多于需要得到的一方
                    //新增一条策略
                    strategys.add(Strategy.create(needPayResult.getUserName(),needGetResult.getUserName(),needGetResult.getNeedGet().abs()));
                    //更新需要支付的一方
                    needPayResult.setNeedGet(needPayResult.getNeedGet().add(needGetResult.getNeedGet()));
                    //移除需要得到的一方
                    needGetIterator.remove();
                }
            }

        }
        return results;
    }

    private List<UserEntity> getMemberOfGroup(Integer groupId) {
        List<UserGroupRelationEntity> relations =
                relationService.getByGroupId(groupId.toString(),
                        new OffsetQuery<>());


        Collection<UserEntity> userEntities = userService.listByIds(relations.stream().map(UserGroupRelationEntity::getUserId
        ).collect(Collectors.toList()));
        return new ArrayList<>(userEntities);
    }

    //先获取组内的所有成员，交易记录根据创建人分组，得到每组中钱数最多的纪录
    private void getMostExpensiveRecordOfMember(Map<String, Object> dataMap,
                                                List<CashRecordOutbound> cashRecords,
                                                CashRecordInbound record) {
        GroupEntity group = groupService.getById(record.getGroupId());
        if(StringUtils.equals(group.getGroupType(),"default")){
            return;
        }
        //将记录根据用户分组
        Map<Long, List<CashRecordOutbound>> recordsOfUsers =
                cashRecords.stream().collect(groupingBy(CashRecordOutbound::getUserId));
        //创建一个集合存储每个成员的最大消费记录
        List<CashRecordOutbound> memberMaxRecordList = new ArrayList<>(recordsOfUsers.size());
        for(Map.Entry<Long,List<CashRecordOutbound>> entry : recordsOfUsers.entrySet()){
            List<CashRecordOutbound> records = entry.getValue();
            CashRecordOutbound maxRecord = getMaxAmountCashRecord(records);
            memberMaxRecordList.add(maxRecord);
        }
        getUserInfo(memberMaxRecordList);
        dataMap.put("mostExpensiveRecordOfMember",memberMaxRecordList);

    }
    //根据组类型判断如果是默认组，则记录都是个人的，否则根据用户分组，获取最大钱数的记录，并封装用户信息
    private void getSelfMostExpensiveRecord(Map<String, Object> dataMap,
                                            List<CashRecordOutbound> cashRecords,
                                            CashRecordInbound record) {
        GroupEntity group = groupService.getById(record.getGroupId());
        if(StringUtils.equals(group.getGroupType(),"default")){
            CashRecordOutbound maxRecord = getMaxAmountCashRecord(cashRecords);
            getUserInfo(Arrays.asList(maxRecord));
            dataMap.put("selfMostExpensiveRecord",maxRecord);
        }
    }

    private CashRecordOutbound getMaxAmountCashRecord(List<CashRecordOutbound> cashRecords) {
        return cashRecords.stream().max(Comparator.comparing(CashRecordOutbound::getAmount)).get();
    }

    //对消费类型分组，统计总金额
    private void getConsumeTypeDistribution(Map<String, Object> dataMap,
                                            List<CashRecordOutbound> cashRecords) {
        Map<String, List<CashRecordOutbound>> recordMap =
                cashRecords.stream().collect(groupingBy(CashRecordOutbound::getConsumeType));
        Map<String,BigDecimal> sumData = new HashMap<>(5);
        for(Map.Entry<String, List<CashRecordOutbound>> entry : recordMap.entrySet()){
            List<CashRecordOutbound> records = entry.getValue();
            if(CollectionUtils.isEmpty(records)){
                continue;
            }
            BigDecimal sum = records.stream().map(CashRecordOutbound::getAmount).reduce(BigDecimal.ZERO,
                    (o1,o2) -> o1.add(o2));
            sumData.put(records.get(0).getConsumeType(),sum);
        }
        dataMap.put("consumeTypeDistribution",sumData);
    }

    private void getUserInfo(List<CashRecordOutbound> recordPageData) {
        if(CollectionUtils.isEmpty(recordPageData)){
            return;
        }
        Set<Long> userIds =
                recordPageData.stream().map(CashRecordOutbound::getUserId).collect(Collectors.toSet());
        Map<String, UserEntity> userMap = userService.getUserMapByIds(userIds);
        for(CashRecordOutbound record : recordPageData){
            record.setCreateName(userMap.get(record.getUserId()).getUserName());
            record.setCreatePhone(userMap.get(record.getUserId()).getPhone());
        }
    }
}
